#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <sys/types.h>

#include <sys/resource.h>    /*setrlimit */
#include <signal.h>
#include <fcntl.h>

#define bool  int    //linux C中没有bool类型
#define false 0      //linux C中没有bool类型
#define true  1      //linux C中没有bool类型
#define IPADDRESS   "0.0.0.0"
#define PORT        8080
#define MAXSIZE     1024
#define LISTENQ     512
#define FDSIZE      1024
#define EPOLLEVENTS 60000
#define MAXCONN     60000

//函数声明
//创建套接字并进行绑定
int socket_bind(const char* ip,int port);
//处理接收到的连接
 void handle_accpet(int epollfd,int listenfd);
//读处理
void do_read(int epollfd,int fd,char *buff);
//写处理
void do_write(int epollfd,int fd,char *buff);
//添加事件
void add_event(int epollfd,int fd,int state);
//修改事件
void modify_event(int epollfd,int fd,int state);
//删除事件
void delete_event(int epollfd,int fd,int state);

void init_signal(void)//设置信号处理,SIG_IGN表示忽略信号,SIG_DFL表示使用信号的默认处理方式
{
    signal(SIGCHLD, SIG_DFL);
    signal(SIGPIPE, SIG_IGN);
}

int set_fdlimit()
{
    //设置每个进程允许打开的最大文件数
    //这项功能等价于linux终端命令 "ulimit -n 102400"
    struct rlimit rt;
    rt.rlim_max = rt.rlim_cur = MAXCONN;
    if (setrlimit(RLIMIT_NOFILE, &rt) == -1)
    {
        perror("setrlimit error");
        return -1;
    }

    return 0;
}

int main(int argc,char *argv[])
{
    //设置信号处理
    init_signal();
    //设置每个进程允许打开的最大文件数,socket
    if (set_fdlimit() < 0)
    {
        return -1;
    }

    int listenfd = socket_bind(IPADDRESS,PORT);
    listen(listenfd,LISTENQ);
    struct epoll_event events[EPOLLEVENTS];
    char buff[MAXSIZE];
    memset(buff,0,MAXSIZE);
    //创建一个描述符
    int epollfd = epoll_create(FDSIZE);
    //添加监听描述符事件
    add_event(epollfd,listenfd,EPOLLIN);
    while(1)
    {
        //获取已经准备好的描述符事件
        int n = epoll_wait(epollfd,events,EPOLLEVENTS,-1);
        for(int i = 0;i < n;i++) //进行选好遍历
        {
            int fd = events[i].data.fd;
            //根据描述符的类型和事件类型进行处理
            if ((fd == listenfd) &&(events[i].events & EPOLLIN))
            {
                handle_accpet(epollfd,listenfd); //处理接受事件
            }else if (events[i].events & EPOLLIN)
            {
                do_read(epollfd,fd,buff);         //处理读事件
            }else if (events[i].events & EPOLLOUT)
            {
                do_write(epollfd,fd,buff);        //处理写事件
            }
        }
    }
    close(epollfd);
    return 0;
}
int socket_bind(const char* ip,int port)
{
    int  listenfd;
    struct sockaddr_in servaddr;
    listenfd = socket(AF_INET,SOCK_STREAM,0);
    if (listenfd == -1)
    {
        perror("socket error:");
        exit(1);
    }
    //一个端口释放后会等待两分钟之后才能再被使用，SO_REUSEADDR是让端口释放后立即就可以被再次使用。
    int reuse_addr = 1;
    if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr)) == -1)
    {
        return -1;
    }
    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//绑定所有网卡所有IP
    servaddr.sin_port = htons(port);
    if (bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) == -1)
    {
        perror("bind error: ");
        exit(1);
    }
    printf("listen on: %d,listenfd=%d\n",PORT,listenfd);
    return listenfd;
}

void handle_accpet(int epollfd,int listenfd)
{
    int clifd;
    struct sockaddr_in cliaddr;
    socklen_t  cliaddrlen = sizeof(cliaddr);
    clifd = accept(listenfd,(struct sockaddr*)&cliaddr,&cliaddrlen);
    if (clifd == -1)
        perror("accpet error:");
    else
    {
        printf("accept a new client: %s:%d,fd=%d\n",inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port,clifd);
        //添加一个客户描述符和事件
        add_event(epollfd,clifd,EPOLLIN);
    }
}
void do_read(int epollfd,int fd,char *buff)
{
    int n = read(fd,buff,MAXSIZE);
    if (n == -1)
    {
        perror("read error:");
        close(fd);
        delete_event(epollfd,fd,EPOLLIN);
    }
    else if (n == 0)
    {
        fprintf(stderr,"client close,fd=%d\n",fd);
        close(fd);
        delete_event(epollfd,fd,EPOLLIN);
    }
    else
    {
        printf("read message is: %s,fd=%d\n",buff,fd);
        for(int i = 0; i < n; i++)
        {
            if((buff[i] > ('a'-1)) && (buff[i] < ('z'+1)))
            {
                buff[i] -= 32;
            }

        }
        //修改描述符对应的事件，由读改为写
        modify_event(epollfd,fd,EPOLLOUT);
    }
}
void do_write(int epollfd,int fd,char *buff)
{
    int nwrite;
    nwrite = write(fd,buff,strlen(buff));
    if (nwrite == -1)
    {
        perror("write error:");
        close(fd);
        delete_event(epollfd,fd,EPOLLOUT);
    }else
    {
        modify_event(epollfd,fd,EPOLLIN);
    }
    memset(buff,0,MAXSIZE);
}
void add_event(int epollfd,int fd,int state)
{
    struct epoll_event ev;
    ev.events = state;//LT
    ev.data.fd = fd;
    epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&ev);
}
void delete_event(int epollfd,int fd,int state)
{
    struct epoll_event ev;
    ev.events = state;
    ev.data.fd = fd;
    epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,&ev);
}
void modify_event(int epollfd,int fd,int state)
{
    struct epoll_event ev;
    ev.events = state;
    ev.data.fd = fd;
    epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&ev);
}
